//=========================================================================== // CHAOS GAME SONIFIED // Nick Gessler // 10 September 2012 //=========================================================================== #include #pragma hdrstop #include "Unit1.h" #include "math.h" #include // include the Multi Media System library //#include //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //=========================================================================== // VARIABLES //=========================================================================== int numberOfCities; int cityX[100]; int cityY[100]; int target; int cmpX, cmpY; int world[500][500]; int iterations; int colorMultiplier = 1; double campX = 250; double campY = 250; double maxWorld = 1; double ratio; double from = 0.50; double to = 0.50; double step = 0.001; AnsiString palette = "Blue to Red"; //---------------------------------------------------- we need these for MIDI int midiport = 0; HMIDIOUT device; union { public: unsigned long word; unsigned char data[4]; } message; int instrument = 32; // "music box" int note; int lastNote; int line; int milliseconds = 130; bool sustain = false; bool probe = false; //=========================================================================== // FUNCTIONS //=========================================================================== //-------------------------------------------------------------- Draw Borders void drawBorders (void) { Form1->PaintBox1->Canvas->MoveTo(0, 0); Form1->PaintBox1->Canvas->LineTo(0, 499); Form1->PaintBox1->Canvas->LineTo(499, 499); Form1->PaintBox1->Canvas->LineTo(499, 0); Form1->PaintBox1->Canvas->LineTo(0, 0); } //-------------------------------------------------------- MIDI RAMP FUNCTION int midiRamp(int part, int whole) { // there are 47 MIDI notes which range from 35 to 81 note = (part * 47) / whole; note = note + 35; return note; } //--------------------------------------------------- Color Ramp Version 2012 TColor colorRamp(int part, int whole) { if (whole == 0) whole++; // prevent divide by zero part = part % whole; // keep part <= whole int pixelDistanceAlongEdges = (part * 1792) / whole; int red, green, blue; // Which edge of the palette cube are we on? if (pixelDistanceAlongEdges < 256) { // from BLACK to BLUE red = 0; green = 0; blue = pixelDistanceAlongEdges; } else if (pixelDistanceAlongEdges < 512) { // from BLUE to CYAN red = 0; green = pixelDistanceAlongEdges - 256; blue = 255; } else if (pixelDistanceAlongEdges < 768) { // from CYAN to GREEN red = 0; green = 255; blue = 255 - (pixelDistanceAlongEdges - 512); } else if (pixelDistanceAlongEdges < 1024) { // from GREEN to YELLOW red = (pixelDistanceAlongEdges - 768); green = 255; blue = 0; } else if (pixelDistanceAlongEdges < 1280) { // from YELLOW to RED red = 255; green= 255-(pixelDistanceAlongEdges - 1024); blue = 0; } else if (pixelDistanceAlongEdges < 1536) { // from RED to MAGENTA red = 255; green= 0; blue = pixelDistanceAlongEdges - 1280; } else { // from MAGENTA to WHITE red = 255; green = pixelDistanceAlongEdges - 1537; blue = 255; } return static_cast(RGB(red, green, blue)); } //---------------------------------------------------- Gray Ramp Version 2012 TColor grayRamp(int part, int whole) { if (whole == 0) whole = 1; // prevent divide by zero part = part % whole; // keep part less than whole int distance = ( part * 255 ) / whole; return static_cast(RGB(distance, distance, distance)); } //--------------------------------------------------------------------- Color void color (AnsiString palette) { for (int i = 0; i < 500; i++) { for (int j = 0; j < 500; j++) { if (palette == "Blue to Red") { Form1->PaintBox1->Canvas->Pixels[i][j] = colorRamp(world[i][j] * colorMultiplier, maxWorld); } if (palette == "Red to Blue") { Form1->PaintBox1->Canvas->Pixels[i][j] = colorRamp((maxWorld - world[i][j]) * colorMultiplier, maxWorld); } if (palette == "Black to White") { Form1->PaintBox1->Canvas->Pixels[i][j] = grayRamp(world[i][j] * colorMultiplier, maxWorld); } if (palette == "White to Black") { Form1->PaintBox1->Canvas->Pixels[i][j] = grayRamp((maxWorld - world[i][j]) * colorMultiplier, maxWorld); } // Faux Shadows if (palette == "Faux Shadows") { if (i == 0 || j == 0) continue; if (world[i-1][j-1] > world[i][j]) { Form1->PaintBox1->Canvas->Pixels[i][j] = clBlack; } else Form1->PaintBox1->Canvas->Pixels[i][j] = clWhite; } // Log values if (palette == "Log Blue to Red") { if (world[i][j] == 0) Form1->PaintBox1->Canvas->Pixels[i][j] = clBlack; else {Form1->PaintBox1->Canvas->Pixels[i][j] = colorRamp(100 * log(world[i][j]), 100 * log(maxWorld)); } } if (palette == "Log Red to Blue") { if (world[i][j] == maxWorld) Form1->PaintBox1->Canvas-> Pixels[i][j] = clBlack; else {Form1->PaintBox1->Canvas->Pixels[i][j] = colorRamp(100 * log(maxWorld - world[i][j]), 100 * log(maxWorld)); } } if (palette == "Log Black to White") { if (world[i][j] == 0) Form1->PaintBox1->Canvas->Pixels[i][j] = clBlack; else {Form1->PaintBox1->Canvas->Pixels[i][j] = grayRamp(100 * log(world[i][j]), 100 * log(maxWorld)); } } if (palette == "Log White to Black") { if (world[i][j] == maxWorld) Form1->PaintBox1->Canvas-> Pixels[i][j] = clBlack; else {Form1->PaintBox1->Canvas->Pixels[i][j] = grayRamp(100 * log(maxWorld - world[i][j]), 100 * log(maxWorld)); } } // Neighbor Shadows if (palette == "Gray Neighbor Shadows") { if (i == 0 || j == 0 || i == 499 || j == 499) continue; float sum = 0; for (int m = -1; m <= 1; m++) { for (int n = -1; n <= 1; n++) { if (m == i && n == j) continue; sum += world[m][n]; } } float ave = sum / 7; if (world[i][j] == 0) { Form1->PaintBox1->Canvas->Pixels[i][j] = clBlack; } else { Form1->PaintBox1->Canvas->Pixels[i][j] = grayRamp(((ave - world[i][j]) / world[i][j]), colorMultiplier); } } if (palette == "Color Neighbor Shadows") { if (i == 0 || j == 0 || i == 499 || j == 499) continue; float sum = 0; for (int m = -1; m <= 1; m++) { for (int n = -1; n <= 1; n++) { if (m == i && n == j) continue; sum += world[m][n]; } } float ave = sum / 7; if (world[i][j] == 0) { Form1->PaintBox1->Canvas->Pixels[i][j] = clBlack; } else { Form1->PaintBox1->Canvas->Pixels[i][j] = colorRamp(((ave - world[i][j]) / world[i][j]), colorMultiplier); } } } } drawBorders(); } //----------------------------------------------------------------------- Run void run (void) { for (ratio = from; ratio <= to; ratio += step) { for (int i = 0; i < 100000; i++) { iterations++; if (iterations < 10000) continue; target = random(numberOfCities); campX += (cityX[target] - campX) * ratio; cmpX = Int(campX); campY += (cityY[target] - campY) * ratio; cmpY = Int(campY); if (campX < 500 && campY < 500 && campX >= 0 && campY >= 0) { world[cmpX][cmpY]++; if (world[cmpX][cmpY] > maxWorld) { maxWorld = world[cmpX][cmpY]; } } } if (to != from) { Form1->ProgressBar1->Position = 100 * ((ratio - from) / (to - from)); } } Form1->EditMaxHits->Text = FloatToStrF(maxWorld, ffNumber, 10, 0); Form1->EditIterations->Text = FloatToStrF(iterations, ffNumber, 10, 0); Beep(1000, 50); color(palette); Form1->ProgressBar1->Position = 0; } //--------------------------------------------------------------------- Reset void reset (void) { Form1->PaintBox1->Refresh(); campX = 250; campY = 250; maxWorld = 1; iterations = 0; Form1->EditIterations->Text = iterations; Form1->EditMaxHits->Text = 0; Form1->ProgressBar1->Position = 0; numberOfCities = 0; for (int i = 0; i < 500; i++) { for (int j = 0; j < 500; j++) { world[i][j] = 0; } } drawBorders(); } //--------------------------------------------------------------------- Quiet void quiet (void) { for (int j = 0; j < 100; j++) { message.data[0] = 0x80; // note off message.data[1] = j; // for each key midiOutShortMsg(device, message.word); } } //--------------------------------------------------------------- Play Melody void playMelody (void) { for (int i = 0; i < 500; i++) { Form1->PaintBox1->Canvas->Pixels[i][line] = clWhite; Application->ProcessMessages(); Sleep(milliseconds); if (!sustain && (world[i][line] != 0) && (note != lastNote)) { message.data[0] = 0x80; // turn off message.data[1] = note; // previous note midiOutShortMsg(device, message.word); // do it now } if (world[i][line] == 0) continue; // 0 should be silent lastNote = note; note = midiRamp(100 * log(world[i][line]), 100 * log(maxWorld)); if (note != lastNote) { message.data[0] = 0x90; // turn on message.data[1] = note; // new note midiOutShortMsg(device, message.word); // do it now } } message.data[0] = 0x80; // turn off message.data[1] = note; // previous note midiOutShortMsg(device, message.word); // do it now palette = "Log Blue to Red"; color(palette); quiet(); } //=========================================================================== // EVENT HANDLERS //=========================================================================== //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { midiOutOpen(&device, midiport, 0, 0, CALLBACK_NULL); message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument; // instrument message.data[2] = 100; // once set, no need to repeat message.data[3] = 0; // once set, no need to repeat midiOutShortMsg(device, message.word); } //--------------------------------------------------------------------------- void __fastcall TForm1::PaintBox1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if (Button == 0) { cityX[numberOfCities] = X; cityY[numberOfCities] = Y; numberOfCities ++; Form1->PaintBox1->Canvas->Pixels[X][Y] = clBlack; } if (Button == 1) { line = Y; playMelody(); } } //--------------------------------------------------------------------------- void __fastcall TForm1::FormPaint(TObject *Sender) { drawBorders(); } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonRunClick(TObject *Sender) { run(); } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonResetClick(TObject *Sender) { reset(); } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarColorMultiplierChange(TObject *Sender) { colorMultiplier = TrackBarColorMultiplier->Position; EditColorScale->Text = TrackBarColorMultiplier->Position; Timer1->Enabled = true; } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarFromChange(TObject *Sender) { from = TrackBarFrom->Position / 100.0; EditFrom->Text = FloatToStrF(from, ffNumber, 1, 2); } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarToChange(TObject *Sender) { to = TrackBarTo->Position / 100.0; EditTo->Text = FloatToStrF(to, ffNumber, 1, 2); } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarStepChange(TObject *Sender) { step = TrackBarStep->Position / 1000.0; EditStep->Text = FloatToStrF(step, ffNumber, 1, 3); } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioGroupColorClick(TObject *Sender) { if (RadioGroupColor->ItemIndex == 0) { palette = "Blue to Red"; } if (RadioGroupColor->ItemIndex == 1) { palette = "Red to Blue"; } if (RadioGroupColor->ItemIndex == 2) { palette = "Black to White"; } if (RadioGroupColor->ItemIndex == 3) { palette = "White to Black"; } if (RadioGroupColor->ItemIndex == 4) { palette = "Faux Shadows"; } if (RadioGroupColor->ItemIndex == 5) { palette = "Log Blue to Red"; } if (RadioGroupColor->ItemIndex == 6) { palette = "Log Red to Blue"; } if (RadioGroupColor->ItemIndex == 7) { palette = "Log Black to White"; } if (RadioGroupColor->ItemIndex == 8) { palette = "Log White to Black"; } if (RadioGroupColor->ItemIndex == 9) { palette = "Gray Neighbor Shadows"; } if (RadioGroupColor->ItemIndex == 10) { palette = "Color Neighbor Shadows"; } color(palette); } //--------------------------------------------------------------------------- void __fastcall TForm1::Timer1Timer(TObject *Sender) { color(palette); Timer1->Enabled = false; } //--------------------------------------------------------------------------- void __fastcall TForm1::PaintBox1MouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { Form1->EditProbe->Text = FloatToStrF(world[X][Y], ffNumber, 10, 0); if (probe) { if (!sustain && (note != lastNote)) { message.data[0] = 0x80; // turn off message.data[1] = note; // previous note midiOutShortMsg(device, message.word); // do it now } if (world[X][Y] != 0) { // 0 should be silent lastNote = note; note = midiRamp(100 * log(world[X][Y]), 100 * log(maxWorld)); if (note != lastNote) { message.data[0] = 0x90; // turn on message.data[1] = note; // new note midiOutShortMsg(device, message.word); // do it now } } } } //----------------------------------------------------------------------- End void __fastcall TForm1::TrackBarMIDIDelayChange(TObject *Sender) { milliseconds = TrackBarMIDIDelay->Position; Form1->EditMIDIDelay->Text = milliseconds; } //--------------------------------------------------------------------------- void __fastcall TForm1::TrackBarInstrumentChange(TObject *Sender) { instrument = TrackBarInstrument->Position; Form1->EditInstrument->Text = instrument; message.data[0] = 0xC0; // choose instrument command message.data[1] = instrument; // instrument midiOutShortMsg(device, message.word); } //--------------------------------------------------------------------------- void __fastcall TForm1::ButtonQuietClick(TObject *Sender) { quiet(); } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioGroupSustainClick(TObject *Sender) { sustain = RadioGroupSustain->ItemIndex; } //--------------------------------------------------------------------------- void __fastcall TForm1::RadioGroupProbeClick(TObject *Sender) { probe = RadioGroupProbe->ItemIndex; } //--------------------------------------------------------------------------- void __fastcall TForm1::EditMIDIDelayChange(TObject *Sender) { milliseconds = StrToInt(EditMIDIDelay->Text); Form1->TrackBarMIDIDelay->Position = milliseconds; } //--------------------------------------------------------------------------- void __fastcall TForm1::EditInstrumentChange(TObject *Sender) { instrument = StrToInt(EditInstrument->Text); Form1->TrackBarInstrument->Position = instrument; } //---------------------------------------------------------------------------